import ply.yacc as yacc
import js_lexer 
from js_type import *
from block_table import BlockTable
from inter_code_gen import ICGen

tokens = js_lexer.tokens

# Parsing rules

precedence = (
    ('left','PLUS','MINUS'),
    ('left','TIMES'),
    #('left','TIMES','DIVIDE'),
    ('right','UMINUS'),
    )

#create an instance of intermediate code generator
icg = ICGen()

btEntryStack = []
# instead of a stack with symbol table, we have a stack with block table entries 

blockTable = BlockTable()
# create main BlockTable and main block table entry
# all functions and global variables will be "stored" here
currentBtEntry = blockTable.getMainBlockTableEntry()
btEntryStack.append(currentBtEntry)
depth = 1
yacc.semanticError = 0

class ParseError(Exception):
	"Excepcion lanzada cuando se produce un error sintactico"
	def __init__(self,msg):
		self.msg = msg

	def __str__(self):
	 	return repr(self.msg)

def p_axioma_s(p):
	'''a : s'''
	icg.writeToFile()
	#print "writeToFile"


def p_axioma_sa(p):
	'''a : s a'''
	#DEBUG
	#print "a: s a"
	#DEBUG
	pass

def p_axioma_def_function(p):
	'''s : def_function ending'''

	#DEBUG
	#print "s : def_function ending"
	#FINDEBUG

	icg.writeToFile()
	#print "writeToFile"
	#blockTable.printAll()

def p_axioma_lambda(p):
	'''a : lambda'''
	# do nothing
	pass

def p_def_function(p):
	'''def_function : def_function_params ending LBRACE ending c RBRACE'''
	p[0] = p[5]

	btEntry = btEntryStack[-1]

	btEntryStack.pop()
	global depth

	depth -= 1
	# decrement depth after exiting function declaration

	if (depth < 1):
		errorMsg = "Error del compilador. La profundidad en las tablas de simbolos no puede ser menor que 1"
		printSemanticError(p.lexer.lineno,errorMsg)
		raise SyntaxError

	icg.emitLabel("END_FUNCTION_"+btEntry.getName())
	btEntry.getSymbolTable().finish()

	#DEBUG
	#print "def_function : def_function_params ending LBRACE ending c RBRACE"
	#FINDEBUG

def p_def_function_declaration_params(p):
	'''def_function_params : def_function_declaration params_def RPAREN'''

	#DEBUG
	#print "def_function_params : def_function_declaration params_def RPAREN"
	#FINDEBUG

	#params_def (p[2]) is a list with params types

	functionName = p[1]
	currentBtEntry = btEntryStack[-1]

	functionType = None
	functionType = currentBtEntry.getType(functionName)

	currentBtEntry.setType(functionName,functionType)
	currentSt = currentBtEntry.getSymbolTable()

	for dparam in p[2]:
		lexeme = dparam['lexeme']
		#print "Param:",lexeme
		currentSt.setParam(lexeme)
		


def p_def_function_declaration(p):
	'''def_function_declaration : FUNCTION ID LPAREN'''

	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insert(p[2],FunctionType())
	icg.emitLabel("FUNCTION_"+p[2])

	#new st is set
	btEntry = blockTable.addBlockTableEntry(p[2],currentBtEntry)

	global depth

	depth += 1
	#new function declaration so depth is incremented
	btEntry.setDepth(depth)

	if (depth > 2):
		errorMsg = "No se permiten funciones anidadas"
		printSemanticError(p.lexer.lineno,errorMsg)
		raise SyntaxError

	btEntryStack.append(btEntry)
	#print "se ha added una nueva entrada de la tabla de bloques:",btEntry.getName()

	p[0] = p[2]


def p_c_sc(p):
	'''c : s c'''
	#instruccion dentro de un bloque {}

	if p[2] == None:
		errorMsg = "Error al generar un bloque"
		printSemanticError(p.lexer.lineno,errorMsg)
		raise SyntaxError
	
	if isinstance(p[2]['type'],str):
		if  p[2]['type'] != "lambda":
			errorMsg = "error al ejecutar la produccion c : s c. No es lambda"
			printSemanticError(p.lexer.lineno,errorMsg)
			raise SyntaxError
		#if lamda, c = s
		p[0] = p[1]
	else:
		# c = current s which is c
		p[0] = p[2]

	#DEBUG
	#print "c : s c"
	#FINDEBUG


def p_c_lambda(p):
	'''c : lambda'''
	p[0] = {'type' : "lambda",
		'post' : 0 #TODO check this
		}
	#DEBUG
	#print "c: lambda"
	#DEBUG

def p_s_variable(p):
	'''s : variable ending'''
	p[0] = p[1]

	#DEBUG
	#print "s: variable ending"
	#DEBUG

def p_s_i0(p):
	#'''s : i0 eos'''
	#'''s : i0 ending'''
	'''s : i0 ending'''
	p[0] = p[1]

	#DEBUG
	#print "s: i0 eos"
	#DEBUG

def p_s_ipalres(p):
	'''s : ipal_res ending'''
	#print "s : ipal_res ending"
	p[0] = p[1]


def p_ending_eos(p):
	'''ending : eos ending'''

	#DEBUG
	#print "ending"
	#FINDEBUG
	pass


def p_ending_lambda(p):
	'''ending : lambda'''
	#'''ending : eos'''
	pass

def p_eos(p):
	'''eos : SEMI
	   | NLINE'''
	pass


def p_variable_asign(p):
	'''variable : VAR iasig'''
	p[0] = p[2]

def p_variable(p):
	'''variable : VAR params_def'''
	for dparam in p[2]:
		icg.emitAssignment(dparam['lexeme'],0)
		#init to 0

#def p_variable(p):
#	'''variable : VAR rvar'''
#	p[0] = p[2]
#
#def p_variable_mval(p):
#	'''variable : VAR ID mval'''
#	p[0] = p[1]
#
#	#TODO
#	pass

#def p_rvar_assign(p):
#	'''rvar : iasig mval'''
#	# type was already set in iasig production 
#	
#	lTypes = p[2]['type']
#	lTypes.append(p[1]['type'])
#
#	p[0] = {'type' : p[2]['type'],
#		'post' : None
#		}
#
#	#DEBUG
#	print "rvar",p[1]
#	#DEBUG

#def p_mval_comma(p):
#	'''mval : COMMA variable'''
#	#TODO comprobar que var a, var b inserta el tipo en la tabla de simbolos
#	pass
#
#def p_mval_lambda(p):
#	'''mval : lambda'''
#	p[0] = {'type' : [],
#		'post' : None
#		}
#
#	#DEBUG
#	print "mval"
#	#DEBUG

def p_iasig_id_id(p):
	'''iasig : ID PERIOD ID asig'''

	lexeme = p[1]

	currentBtEntry = btEntryStack[-1]
	objectType = currentBtEntry.getType(lexeme)

	if (not isinstance(objectType,ObjectType)):
		errorMsg = "Error en la asignacion.:%s no es un objeto"%p[1]
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError
		
	symbolTable = objectType.getSymbolTable()

	if symbolTable.getPos(p[3]) == None:
		errorMsg = "Error en la asignacion. No existe el atributo:%s en la clase:%s"%(p[3],objectType.getClassName())
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError


	else:
		symbolTable.setType(lexeme,p[4]['type'])	

	post = p[1]+"."+symbolTable.getPost(p[3])
	
	icg.emitAssignment(post,p[4]['post'])

	p[0] = {
		'type' : p[4]['type'],
		'post' : post
		}	
	#DEBUG
	#print "ID asig post:",post
	#FINDEBUG

def p_iasig_id(p):
	'''iasig : ID asig'''


	lexeme = p[1]

	#DEBUG
	#print "-------------------------------------------------------------"
	#print "iasig : ID asig"
	#print lexeme
	#print "-------------------------------------------------------------"
	#FINDEBUG


	currentBtEntry = btEntryStack[-1]
	isObject = False

	if p[2]['type'] == None:
		# this just happens when asig is a function called for a void function 
		errorMsg = "Error en la asignacion. La funcion es void, no retorna ningun valor"
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError
		

	if not currentBtEntry.isDefined(lexeme):
		currentBtEntry.insert(lexeme,p[2]['type'])
	else:
		currentBtEntry.setType(lexeme,p[2]['type'])	

	if isinstance(p[2]['type'],ObjectType):
		symbolTable = p[2]['type'].getSymbolTable()
		symbolTable.setName(p[1])
		isObject = True

	post = currentBtEntry.getPost(lexeme)
	
	if not isObject:
		icg.emitAssignment(post,p[2]['post'])
	else:
		icg.emitIndirectAssignment(post,p[2]['post'])

	p[0] = {
		'type' : p[2]['type'],
		'post' : post
		}	
	#DEBUG
	#print "ID asig post:",post
	#FINDEBUG

def p_iasig_this(p):
	'''iasig : THIS PERIOD ID asig'''
	#set attribute and in the constructor
	# ID is the attribute


	currentBtEntry = btEntryStack[-1]

	constructorName = currentBtEntry.getName()

	lexeme = "this." + p[3]

	if (currentBtEntry.getFather() == None):
		#main block? cannot use THIS token here
		errorMsg = "Error al usar la palabra reservada this fuera de un objeto"
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	fatherBtEntry = currentBtEntry.getFather()

	fatherBtEntry.setType(constructorName,ObjectType(constructorName))

	if not currentBtEntry.isDefined(lexeme):
		currentBtEntry.insert(lexeme,p[4]['type'])
	else:
		currentBtEntry.setType(lexeme,p[4]['type'])	

	post = currentBtEntry.getPost(lexeme)

	currentSt = currentBtEntry.getSymbolTable()
	currentSt.setAttribute(lexeme)
	
	icg.emitAssignment(post,p[4]['post'])

	p[0] = {
		'type' : p[4]['type'],
		'post' : post
		}	


def p_asig(p):
	'''asig : ASSIGN fasig'''
	p[0] = p[2]
		

def p_fasig_i1(p):
	'''fasig : i1'''
	#TODO conflicto  con i0 : i1 podria ser directamente asig : ASSIGN i1
	p[0] = p[1]

def p_fasig_new(p):
	'''fasig : mem_alloc LPAREN param_exe RPAREN'''

	objectType = p[1]['type']

	if(not isinstance(objectType,ObjectType)):
		errorMsg = "Se intenta ejecutar un constructor:%s no declarado"%p[1]
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	symbolTable = objectType.getSymbolTable()

	paramsTypes = symbolTable.getParamsTypes()

	params = p[3]

	className = objectType.getClassName()

	if (len(paramsTypes) != len(params)):
		errorMsg = "El numero de parametros para ejecutar el constructor:%s es incorrecto. Se esperaban:%d y se estan pasando:%d"%(className,len(paramsTypes),len(params))
		printSemanticError(p.lexer.lineno,errorMsg)
		raise SyntaxError

	if(not symbolTable.checkTypes(params)):
		errorMsg = "El tipo de los parametros para ejecutar el constructor:%s es incorrecto. Se esperaban:%s"%(objectType,objectType.paramsTypesStr())
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	for param in reversed(params):
		icg.emitParam(param['post'])


	temporal = icg.emitNewCall(objectType.getClassName(),len(params),objectType)

	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,objectType)

	p[0] = {
		'type' :objectType,
		'post' : temporal
		}

def p_i0_promt(p):
	'''i0 : PROMPT LPAREN ID RPAREN'''

	lexeme = p[3]

	currentBtEntry.insert(lexeme,IntegerType())
	currentSt = currentBtEntry.getSymbolTable()

	icg.emitRead(currentSt.getPost(lexeme))

	#DEBUG
	#print "POS PROMT :",currentSt.getPos(lexeme)
	#FINDEBUG

	p[0] = {
		'type' : currentSt.getType(lexeme),
		'post' : currentSt.getPost(lexeme)
		}

def p_i0_document(p):
	'''i0 : DOCUMENT PERIOD WRITE LPAREN i1 RPAREN'''
	toPrint = p[5]
	icg.emitPrint(toPrint['post'])

	p[0] = {
		'type' : toPrint['type'],
		'post' : toPrint['post']
		}

def p_i0_i1(p):
	'''i0 : i1'''
	p[0] = p[1]

def p_ipalres_iif(p):
	'''ipal_res : i1if'''
	p[0] = p[1]


def p_ipalres_i1for(p):
	'''ipal_res : i1for'''
	p[0] = p[1]

def p_i0_iasig(p):
	'''i0 : iasig'''
	p[0] = p[1]
	
def p_i1_i2(p):
	'''i1 : i2 '''
	#if ri1 == lambda
##	if isinstance(p[1]['type'],str):
##		if (p[1][type] == None or p[1]['type'] != "lambda"):
##			errorMsg = "Error al construir la produccion: deberia ser lambda"
##			printSemanticError(p.lineno,errorMsg)
##			raise SyntaxError
##		#else, then is lambda	

	p[0] = p[1]

def p_i1_ri1(p):
	'''i1 : ri1'''
	p[0] = p[1]

def p_ri1(p):
	'''ri1 : ter_op_else'''
	p[0] = p[1]

def p_ter_op_cond(p):
	'''ter_op_cond : i2 CONDOP'''

	checkedIntType = None
	checkedIntType = isinstance(p[1]['type'],BooleanType) 
	# just bool operators

	errorMsg = "El tipo del token no es un booleano al ejecutar un operador ternario"
	
	if checkedIntType == None:
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	temporal = icg.emitTerOpCond(p[1]['post'])
	p_type = IntegerType()

	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

	p[0] = {
		'type': p[1]['type'],
		#'post' : p[1]['post']
		'post' : temporal
		}

def p_ter_op_if(p):
	'''ter_op_if : ter_op_cond i2'''

	temporal = p[1]['post']
	icg.emitAssignment(temporal,p[2]['post'])

	icg.emitTerOpIf()

	p[0] = {
		'type': p[2]['type'],
		#'post' : p[2]['post']
		'post' : temporal
		}

def p_ter_op_else(p):
	'''ter_op_else : ter_op_if COLON i2'''

	temporal = p[1]['post']
	icg.emitAssignment(temporal,p[3]['post'])
	
	icg.emitTerOpElse()
	
	
	p[0] = {
		'type': p[3]['type'],
		#'post' : p[3]['post']
		'post' : temporal
		}

#def p_ri1_lambda(p):
#	'''ri1 : lambda'''
#	p[0] = {'post' : None,
#		'type' : "lambda"
#		}

def p_i2_and(p):
	'''i2 : i2 AND i3'''
	checkedIntType = isinstance(p[1]['type'],BooleanType) and isinstance(p[3]['type'],BooleanType)
	# just bool operators

	errorMsg = "El tipo del token no es un booleano para una operacion logica"
	semanticError = False
	
	if not checkedIntType:
		printSemanticError(p.lineno(1),errorMsg)
		semanticError = True
		raise SyntaxError

	temporal = icg.emitAnd(p[1]['post'],p[3]['post'])

	p_type = BooleanType()

	p[0] = {
		'type': p_type,
		'post' : temporal
		}

	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

def p_i2_i3(p):
	'''i2 : i3'''
	p[0] = p[1]

def p_i3_eq(p):
	'''i3 : i4 EQ i4 
	   | i4 LT i4'''

	checkedType = False

	if (isinstance(p[1]['type'],IntegerType) and isinstance(p[3]['type'],IntegerType)) or (isinstance(p[1]['type'],IntegerType) and isinstance(p[3]['type'],IntegerType)):
		checkedType = True
	
	# just bool operators

	errorMsg = "El tipo del token no es un booleano o un entero para una operacion logica"
	semanticError = False
	
	if not checkedType:
		printSemanticError(p.lineno(1),errorMsg)
		semanticError = True
		raise SyntaxError

	if p[2] == "<":
		temporal = icg.emitLessThan(p[1]['post'],p[3]['post'])
	else:
		temporal = icg.emitEquals(p[1]['post'],p[3]['post'])

	p_type = BooleanType()

	p[0] = {
		'type': p_type,
		'post' : temporal
		}

	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

def p_i3_i4(p):
	'''i3 : i4'''
	p[0] = p[1]

def p_i4_add(p):
	'''i4 : i4 PLUS i5
	 | i4 MINUS i5'''
	#DONE

	checkedIntType = isinstance(p[1]['type'],IntegerType) and isinstance(p[3]['type'],IntegerType)
	# just int operators

	errorMsg = "El tipo del token no es un entero para una operacion aritmetica"
	semanticError = False
	
	if not checkedIntType:
		printSemanticError(p.lineno(1),errorMsg)
		semanticError = True
		raise SyntaxError

	if p[2] == '+':
		temporal = icg.emitAddition(p[1]['post'],p[3]['post'])
	else:
		temporal = icg.emitSubstraction(p[1]['post'],p[3]['post'])

	p_type = IntegerType()

	p[0] = {
		'type': p_type,
		'post' : temporal
		}
	
	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

def p_i4_i5(p):
	'''i4 : i5'''
	p[0] = p[1]

def p_i5_mul(p):
	'''i5 : i5 TIMES i6'''
	#CHECK no tenemos que implementar la multiplicacion, verdad?

	checkedIntType = isinstance(p[1]['type'],IntegerType) and isinstance(p[3]['type'],IntegerType)
	# just int operators

	errorMsg = "El tipo del token no es un entero para una operacion aritmetica"
	semanticError = False
	
	if not checkedIntType:
		printSemanticError("",errorMsg)
		semanticError = True
		raise SyntaxError

	temporal = icg.emitMultiplication(p[1]['post'],p[3]['post'])

	p_type = IntegerType()

	p[0] = {
		'type': p_type,
		'post' : temporal
		}

	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

def p_i5_i6(p):
	'''i5 : i6'''
	p[0] = p[1]

def p_i6_NUM(p):
	'''i6 : NUMBER'''
	p_type = IntegerType()
	temporal = icg.emitNumber(p[1])
	p[0] = {
		'type': p_type,
		'post' : temporal
		}


	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

def p_i6_UMINUSNUM(p):
	'''i6 : MINUS i6 %prec UMINUS'''
	#'''i6 : MINUS NUMBER %prec UMINUS'''  TODO: check esta era la antigua
	errorMsg = "El tipo del token no es un entero para una operacion aritmetica"
	semanticError = False
	#TODO si el tipo es una funcion, return debe ser entero
	# por defecto todas las funciones son enteros ? revisar docs

	if (isinstance(p[2]['type'],FunctionType)):
	#if a function, must return int
		if(not p[2]['type'].checkReturn[IntegerType()]):
			printSemanticError(p.lineno(2),errorMsg)
			semanticError = True
	else:
	#else must be int
		if (not isinstance(p[2]['type'],IntegerType)):
			printSemanticError(p.lineno(2),errorMsg)
			semanticError = True

	if (not semanticError):
		temporal = icg.emitMinus(p[2]['post'])
		p_type = IntegerType()

		p[0] = {
			'type': p_type,
			'post' : temporal
			}
		
		currentBtEntry = btEntryStack[-1]
		currentBtEntry.insertTemporal(temporal,p_type)

	else:
		raise SyntaxError

def p_i6_BOOL(p):
	'''i6 : TRUE
	 | FALSE'''
	#DONE
	if p[1]== "true":
		p_type = BooleanType()
		temporal = icg.emitTrue()
		
	else:
		p_type = BooleanType()
		temporal = icg.emitFalse()

	p[0] = {
		'type': p_type,
		'post' : temporal
		}
		
	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

def p_i6_STRING(p):
	'''i6 : STRING'''

	if ( len(p[1]) > 100):
		errorMsg = "La longitud de %s es excesiva. No debe superar %d caracteres"%(p[1],100)
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	p_type = StringType()
	temporal = icg.emitString(p[1])


	currentBtEntry = btEntryStack[-1]
	currentBtEntry.insertTemporal(temporal,p_type)

	p[0] = {
		'type': p_type,
		'post' : temporal
		}
	
def p_i6_paren(p):
	'''i6 : LPAREN i1 RPAREN'''
	p[0] = p[1]
	#DONE

def p_i6_this(p):
	'''i6 : THIS PERIOD ID ending'''
	#read access in constructor
	# this is an attribute of an object

	currentBtEntry = btEntryStack[-1]


	if (currentBtEntry.getFather() == None):
		#main block? cannot use THIS token here
		errorMsg = "Error al usar la palabra reservada this en el bloque principal del programa"
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	fatherBtEntry = currentBtEntry.getFather()

	complexType = fatherBtEntry.getType(constructorName)

	if (not isinstance(complexType,ObjectType)):
		errorMsg = "Error al usar la palabra reservada this fuera de un objeto o antes de declararlo"
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	currentSt = currentBtEntry.getSymbolTable()

	lexeme = p[3]


	if (not currentSt.isAttribute(lexeme)):
		errorMsg = "No existe el atributo:%s"%lexeme
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	p[0] = {
		'type': currentSt.getType(lexeme),
		'post': currentSt.getPost(lexeme)
		}

def p_i6_id(p):
	'''i6 : ID'''

	currentBtEntry = btEntryStack[-1]
	currentSt = currentBtEntry.getSymbolTable()

	lexeme = p[1]

	if currentSt.getPos(lexeme) == None:
		#using a not declared variable: integer = 0
		currentBtEntry.insert(lexeme)
		currentBtEntry.setType(lexeme,IntegerType())

	#	# this is hidding : t1 = 0; lexeme = t1
	# antes las tres lines siguientes estaban comentadas
		temporal = icg.emitNumber(0)
		icg.emitAssignment(lexeme,temporal)
		currentBtEntry.insertTemporal(temporal,currentSt.getType(p[1]))

		p_new = {
			'type' : currentSt.getType(p[1]),
			'post' : currentBtEntry.getPost(lexeme)
			}
	else:

		p_new = {
			'type' : currentSt.getType(p[1]),
			'post' : currentSt.getPost(p[1])
			}

	p[0] = p_new



def p_i6_return(p):
	#'''i6 : RETURN end_return'''
	'''i6 : RETURN i1'''

	checkedType = None

	if isinstance(p[2]['type'],BooleanType):
		checkedType = "boolean"
	if isinstance(p[2]['type'],IntegerType):
		checkedType = "integer"

	errorMsg = "Las funciones solo pueden devolver un valor booleano o entero"
	
	if checkedType == None:
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError


	if(p[2] != None):
		icg.emitReturn(p[2]['post'])

		# set return type to integer
		currentBtEntry = btEntryStack[-1]
		functionName = currentBtEntry.getName()
		#DEBUG
		#blockTable.printAll()
		#print "functionName:",functionName
		#FINDEBUG
		functionType = currentBtEntry.getType(functionName)
		functionType.addReturnType()


	p[0] = p[2]



#def p_end_return_i1(p):
#	''' end_return : i1'''
#	p[0] = p[1]
#
#def p_end_return(p):
#	''' end_return : lambda'''
#	p[0] = None

def p_i6_idid(p):
	'''i6 : ID PERIOD ID'''
	#access to p[1] class intance (object), p[3] attribute

	#DEBUG
	#print "6 : ID PERIOD ID"
	#FINDEBUG

	currentBtEntry = btEntryStack[-1]

	objectType = currentBtEntry.getType(p[1])
	
	if(objectType == None):
		errorMsg = "No definido:%s"%p[1]
		printSemanticError(p.lexer.lineno,errorMsg)
		raise SyntaxError


	if (not isinstance(objectType,ObjectType)):
		errorMsg = "Error al acceder a un atributo de un objeto"
		printSemanticError(p.lexer.lineno,errorMsg)
		raise SyntaxError

	className = objectType.getClassName()

	currentSt = objectType.getSymbolTable()

	#DEBUG
	#print "att type:%s"%currentSt.getType(p[3])
	#ENDDEBUG

	p_new = {
		'type' : currentSt.getType(p[3]),
		'post' : p[1]+"."+currentSt.getPost(p[3])
		}

	p[0] = p_new

def p_i6_func(p):
	'''i6 : func_exe'''
	p[0] = p[1]
	
def p_i6_mem(p):
	'''i6 : mem_alloc LPAREN ID iasig LPAREN'''
	# TODO No entiendo esta produccion
	pass

#def p_i6_paraexe(p): #TODO: completarla con mail
#	pass

def p_fexe_param(p):
	'''func_exe : ID LPAREN param_exe RPAREN ending'''
	   
	#DEBUG
	#print "func_exe : ID LPAREN param_exe RPAREN ending"
	#FINDEBUG
	
	currentBtEntry = btEntryStack[-1]

	functionName = p[1]

	if currentBtEntry.getPos(functionName) == None:
		errorMsg = "Se intenta ejecutar una funcion:%s no declarada"%p[1]
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	functionType = currentBtEntry.getType(functionName)

	if(not isinstance(functionType,FunctionType)):
		errorMsg = "Se intenta ejecutar una funcion:%s no declarada"%p[1]
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	symbolTable = blockTable.getSymbolTable(functionName)

	#DEBUG
	#symbolTable.printAll()
	#FINDEBUG

	paramsTypes = symbolTable.getParamsTypes()

	params = p[3]

	if (len(paramsTypes) != len(params)):
		errorMsg = "El numero de parametros para ejecutar la funcion:%s es incorrecto. Se esperaban:%d y se estan pasando:%d"%(functionName,len(paramsTypes),len(params))
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	if(not symbolTable.checkTypes(params)):
		errorMsg = "El tipo de los parametros para ejecutar la funcion:%s es incorrecto. Se esperaban:%s"%(functionType,functionType.paramsTypesStr())
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError



	for param in params:
		icg.emitParam(param['post'])

	# type must be return type from function which can only be None (no return) or simple types

	rType = functionType.getReturnType()

	temporal = icg.emitCall(functionName,len(params),rType)


	if (rType != None):
		# void function
		currentBtEntry = btEntryStack[-1]
		currentBtEntry.insertTemporal(temporal,rType)

	p[0] = {
		'type' :rType,
		'post' : temporal
		}

def p_param_exe(p):
	'''param_exe : i6 p1'''

	params = p[2]

	if (params == None):
		params = []

	params.append(p[1])

	p[0] = params


	#DEBUG
	#print "param_exe : i6 p1"
	#FINDEBUG


def p_p1_p1(p):
#	'''p1 : i6 COMMA p1'''
	'''p1 : COMMA i6 p1'''

	params = p[3]
	if (params == None):
		params = []
	
	params.append(p[2])
				
	p[0] = params

	#DEBUG
	#print "p1 : COMMA i6 p1"
	#FINDEBUG
	
def p_p1_lambda(p):
	'''p1 : lambda'''
	p[0] = None

	#DEBUG
	#print "p1 : lambda"
	#FINDEBUG

def p_mem_alloc(p):
	'''mem_alloc : NEW ID'''
	#ID must be an object
	# TODO: aqui se realiza la operacion de copia de ST y en ID.ID se accede a ella

	className = p[2]
	currentBtEntry = btEntryStack[-1]

	if currentBtEntry.getPos(className) == None:
		errorMsg = "Se intenta instanciar una clase:%s no declarada"%p[1]
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError


	complexType = currentBtEntry.getType(className)

	if(complexType == None):
		#not defined class
		errorMsg = "Error al obtener una instancia de %s. %s no ha sido declarada previamente"%(className,className)
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	elif(not isinstance(complexType,ObjectType)):
		#not a class
		errorMsg = "Error al obtener una instancia de %s. %s no es una clase"%(className,className)
		printSemanticError(p.lineno(1),errorMsg)
		raise SyntaxError

	btEntry = blockTable.getBlockTableEntry(className)
	objectSt = blockTable.getSymbolTable(className).getCopy()

	p[0] = {'type' : ObjectType(className,objectSt),
		'post' : None}

	# post is setting after constructor execution
	

def p_params_def(p):
	'''params_def : ID restid'''

	#DEBUG
	#print "params_def : ID restid"
	#DEBUG

	params = p[2]
	if (params == None):
		params = []

	# by default params are integers
	paramType = IntegerType()
	lexeme = p[1]

	dvalue = { 'type' :paramType,
		   'lexeme':lexeme
		 }

	params = [dvalue] + params
	
	currentBtEntry = btEntryStack[-1]
	currentSt = currentBtEntry.getSymbolTable()


	currentBtEntry.insert(lexeme)
	currentBtEntry.setType(lexeme,paramType)

	p[0] = params	

	#currentSt.setParam(lexeme)

def p_restid_comma(p):
	'''restid : COMMA ID restid'''

	# by default params are integers
	paramType = IntegerType()

	params = p[3]
	if (params == None):
		params = []

	lexeme = p[2]

	dvalue = { 'type' :paramType,
		   'lexeme':lexeme
		 }

	params = [dvalue] + params
	
	currentBtEntry = btEntryStack[-1]
	currentSt = currentBtEntry.getSymbolTable()

	lexeme = p[2]

	currentBtEntry.insert(lexeme)
	currentBtEntry.setType(lexeme,IntegerType())

	#currentSt.setParam(lexeme)

	p[0] = params	

def p_restid_lambda(p):
	'''restid : lambda'''
	p[0] = None

def p_i1if(p):
	'''i1if : i1if_begin c RBRACE'''
	
	p[0] = {
		'type': p[2]['type'],
		'post' : p[2]['post']
		}
	
	icg.emitEndIf();

	# ending block {} so
	# get former block table entry
	#btEntryStack.pop()

def p_i1if_begin(p):
	'''i1if_begin : IF LPAREN i1 RPAREN ending LBRACE ending''' # antes estaba asi
	#'''i1if_begin : IF LPAREN i0 RPAREN ending LBRACE ending'''

	checkedType = None

	if isinstance(p[3]['type'],BooleanType):
		checkedType = "boolean"
	if isinstance(p[3]['type'],IntegerType):
		checkedType = "integer"

	errorMsg = "El tipo del token no es un booleano o entero al ejecutar un IF"
	semanticError = False
	
	if checkedType == None:
		printSemanticError(p.lineno(3),errorMsg)
		semanticError = True
		raise SyntaxError

	temporal = icg.emitIf(p[3]['post'],checkedType)

	p[0] = {
		'type': p[3]['type'],
		'post' : temporal
		}

	currentBtEntry = btEntryStack[-1]
#	btEntry = blockTable.addBlockTableEntry(blockTable.getNewTempStName(),currentBtEntry)
#	btEntryStack.append(btEntry)



def p_i1for(p):
	'''i1for : for_update RPAREN ending LBRACE ending c RBRACE'''
	icg.emitForEnd()
	p[0] = None

def p_for_update(p):
	'''for_update : for_cond iasig'''
	icg.prepareForUpdate()

def p_for_cond(p):
	'''for_cond : for_begin SEMI i1 SEMI '''

	if not isinstance(p[3]['type'],BooleanType):
		errorMsg("El tipo de la condicion del bucle for no es booleano")
		printSemanticError(p.lineno(3),errorMsg)
		raise SyntaxError

	icg.emitForCond(p[3]['post'])
	

def p_for_begin(p):
	'''for_begin : FOR LPAREN initialisation'''
	#icg.emitForBegin(p[3]['post'])
	p[0] = p[3]
	icg.emitForBegin()

def p_inicializacion_iasig(p):
	'''initialisation : iasig'''
	p[0] = p[1]

	
def p_inicializacion_lambda(p):
	'''initialisation : lambda'''
	p[0] = None


def p_lambda(p):
	'''lambda : '''
	pass

def p_error(p):
	print "Error en la sintaxis en token %s:%s en la linea:%d"%(p.type,p.value,p.lineno)
	print "Se descartan los tokens hasta que se encuentre un } o fin de fichero y se continua con el parser"
	#revisar ply 6.8.2 para recuperarse de errores
	while 1:
		tok = yacc.token()	#get next token
		if not tok or tok.type == 'RBRACE' :
			break
	yacc.restart()
	yacc.semanticError = 1

def printSemanticError(lineNumber,msg):
	print "Error  sintactico en la linea:%d"%lineNumber
	print msg
	raise ParseError(msg)

def finishParser():
	icg.finish()
	blockTable.finishMainSymbolTable()

#build grammar
yacc.yacc(method='LALR')
